package concurrentpractice;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import lombok.Data;
import lombok.experimental.Accessors;
import org.junit.Test;

public class LockConditionSolution {

  private enum Printer {
    NUMBER, LETTER
  }

  @Data
  @Accessors(chain = true)
  private class Scheduler {

    private Printer enabledPrinter = Printer.NUMBER;
  }

  private Scheduler scheduler = new Scheduler();

  @Test
  public void verifyLock() throws InterruptedException {

    Lock lock = new ReentrantLock(true);
    Thread numberPrintThread = new Thread(() -> {
      for (int i = 1; i < 52; i = i + 2) {
        lock.lock();
        try {
          if (scheduler.getEnabledPrinter() == Printer.NUMBER) {
            for (int j = 0; j < 2; j++) {
              System.out.print(i + j);
            }
            scheduler.setEnabledPrinter(Printer.LETTER);
          }
        } finally {
          lock.unlock();
        }
      }
    });

    Thread letterPrintThread = new Thread(() -> {
      for (char i = 'A'; i <= 'Z'; i++) {
        lock.lock();
        try {
          if (scheduler.getEnabledPrinter() == Printer.LETTER) {
            System.out.print(i);
            scheduler.setEnabledPrinter(Printer.NUMBER);
          }
        } finally {
          lock.unlock();
        }
      }
    });
    numberPrintThread.start();
    letterPrintThread.start();

    numberPrintThread.join();
    letterPrintThread.join();
  }


  @Test
  public void verifyLockWithCondition() throws InterruptedException {

    Lock lock = new ReentrantLock(true);
    Condition condition = lock.newCondition();
    Thread numberPrintThread = new Thread(() -> {
      for (int i = 1; i < 52; i = i + 2) {
        lock.lock();
        try {
          while (scheduler.getEnabledPrinter() != Printer.NUMBER) {
            condition.wait();
          }
          for (int j = 0; j < 2; j++) {
            System.out.print(i + j);
          }
          scheduler.setEnabledPrinter(Printer.LETTER);
          condition.signal();
        } catch (InterruptedException e) {
          e.printStackTrace();
        } finally {
          lock.unlock();
        }
      }
    });

    Thread letterPrintThread = new Thread(() -> {
      for (char i = 'A'; i <= 'Z'; i++) {
        lock.lock();
        try {
          while (scheduler.getEnabledPrinter() != Printer.LETTER) {
            condition.wait();
          }
          System.out.print(i);
          scheduler.setEnabledPrinter(Printer.NUMBER);
          condition.signal();
        } catch (InterruptedException e) {
          e.printStackTrace();
        } finally {
          lock.unlock();
        }
      }
    });
    numberPrintThread.start();
    letterPrintThread.start();

    numberPrintThread.join();
    letterPrintThread.join();
  }
}
